package com.im

import android.os.Handler
import android.os.Handler.Callback
import android.os.Looper
import android.os.Message
import android.text.TextUtils

import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket

// 客户端
class Client: Socket, Runnable {
    // 发送数据的线程
    private inner class SendThread: Thread {
        private val mData: ImProtocol   // 要发送的数据

        /**
         * 构造方法。
         * @param data  要发送的数据
         */
        constructor(data: ImProtocol) {
            mData = data
        }

        /**
         * 线程方法。
         */
        override fun run() {
            try {
                mSender.writeObject(mData)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    private val mCallback: Handler                      // 回调
    private lateinit var mSender: ObjectOutputStream    // 数据发送者

    companion object {
        /**
         * 获取自己的头像索引。
         */
        var iconIndex = 0   // 头像索引
            private set

        /**
         * 获取自己的名字。
         */
        var username = ""   // 用户名
            private set
    }

    /**
     * 构造方法。
     * @param callback  回调
     */
    constructor(callback: Callback) {
        // 初始化成员
        mCallback = Handler(Looper.getMainLooper(), callback)

        // 开启接收数据的线程
        Thread(this).start()
    }

    /**
     * 添加好友。
     * @param friendName 好友名字
     * @return 结果
     */
    fun addFriend(friendName: String): ResultCode {
        // 检查是否超过好友上限
        if (this.friendCount > Const.MAX_USER_COUNT) {
            return ResultCode.TOO_MANY_FRIENDS
        }

        // 检查好友名
        if (friendName == username) {
            return ResultCode.FRIEND_NAME_EQUAL_SELF_NAME
        }

        // 发送添加好友请求
        val request = AddFriendRequest(friendName, iconIndex, username)
        this.sendData(request)

        return ResultCode.SUCCEEDED
    }

    /**
     * 允许或拒绝添加好友。
     * @param allow     允许则为 true，否则为 false
     * @param request   添加好友请求
     */
    fun allowAddFriend(allow: Boolean, request: AddFriendRequest) {
        // 发送结果
        val addFriendResult = AddFriendResult(iconIndex, request.mDstUsername,
            if (allow) ResultCode.ADD_FRIEND_ALLOW else ResultCode.ADD_FRIEND_NOT_ALLOW, request.mSrcUsername)
        this.sendData(addFriendResult)

        // 如果允许，则好友数加1
        if (allow) {
            this.friendCount++
        }
    }

    /**
     * 聊天。
     * @param chatInfo  聊天信息
     */
    fun chat(chatInfo: ChatInfo) {
        this.sendData(chatInfo)
    }

    /**
     * 关闭客户端。
     */
    override fun close() {
        // 离线
        this.logoff()

        try {
            // 延迟一下，防止数据还没发送完成就关闭网络
            Thread.sleep(100)

            // 关闭
            mSender.close()
            super.shutdownInput()
            super.shutdownOutput()
            super.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 获取好友数。
     */
    var friendCount = 0
        private set

    /**
     * 处理添加好友请求。
     * @param request    添加好友请求
     */
    private fun handleAddFriendRequest(request: AddFriendRequest) {
        val message = Message.obtain()
        message.what = Const.UM_GET_ADD_FRIEND_REQUEST
        message.obj = request
        mCallback.sendMessage(message)
    }

    /**
     * 处理添加好友结果。
     * @param result    添加好友结果
     */
    private fun handleAddFriendResult(result: AddFriendResult) {
        if (result.mResultCode === ResultCode.ADD_FRIEND_ALLOW && result.mDstIconIndex != -1) {
            this.friendCount++
        }

        val message = Message.obtain()
        message.what = Const.UM_GET_ADD_FRIEND_RESULT
        message.obj = result
        mCallback.sendMessage(message)
    }

    /**
     * 处理聊天。
     * @param chatInfo  聊天信息
     */
    private fun handleChat(chatInfo: ChatInfo) {
        val message = Message.obtain()
        message.what = Const.UM_CHAT_S2C
        message.obj = chatInfo
        mCallback.sendMessage(message)
    }

    /**
     * 处理其他用户的离线请求。
     * @param request   其他用户的离线请求
     */
    private fun handleFriendLogoff(request: LogoffRequest) {
        val message = Message.obtain()
        message.what = Const.UM_FRIEND_LOGOFF
        message.obj = request
        mCallback.sendMessage(message)
    }

    /**
     * 处理其他用户的登录消息。
     * @param friendLogon   其他用户的登录消息
     */
    private fun handleFriendLogon(friendLogon: FriendLogon) {
        val message = Message.obtain()
        message.what = Const.UM_FRIEND_LOGON
        message.obj = friendLogon
        mCallback.sendMessage(message)
    }

    /**
     * 处理登录结果。
     * @param result    登录结果
     */
    private fun handleLogonResult(result: LogonResult) {
        val message = Message.obtain()
        message.what = Const.UM_GET_LOGON_RESULT
        message.obj = result
        mCallback.sendMessage(message)

        if (result.mResultCode === ResultCode.LOGON_SUCCEEDED) {
            iconIndex = result.mIconIndex
            this.friendCount = result.mFriendCount
        } else {
            username = ""
        }
    }

    /**
     * 处理注册结果。
     * @param result    注册结果
     */
    private fun handleRegResult(result: RegResult) {
        if (result.mResultCode !== ResultCode.REG_SUCCEEDED) {
            username = ""
        }

        val message = Message.obtain()
        message.what = Const.UM_GET_REG_RESULT
        message.obj = result
        mCallback.sendMessage(message)
    }

    /**
     * 离线。
     */
    fun logoff() {
        if (TextUtils.isEmpty(username)) {
            return
        }

        val request = LogoffRequest(username)
        val thread = this.sendData(request)
        try {
            thread.join()
        } catch (e: Exception) {
            e.printStackTrace()
        }

        username = ""
    }

    /**
     * 登录。
     * @param username  用户名
     * @param password  密码
     */
    fun logon(username: String, password: String) {
        // 发送登录请求
        val request = LogonRequest(password, username)
        this.sendData(request)

        // 保存用户名
        Companion.username = username
    }

    /**
     * 处理服务器发送来的数据。
     * @param pro    协议
     */
    private fun onGetMessageFromServer(pro: ImProtocol) {
        when (pro.mType) {
            ImProtocol.Type.ADD_FRIEND_REQUEST -> this.handleAddFriendRequest(pro as AddFriendRequest)
            ImProtocol.Type.ADD_FRIEND_RESULT -> this.handleAddFriendResult(pro as AddFriendResult)
            ImProtocol.Type.CHAT -> this.handleChat(pro as ChatInfo)
            ImProtocol.Type.FRIEND_LOGON -> this.handleFriendLogon(pro as FriendLogon)
            ImProtocol.Type.LOGOFF -> this.handleFriendLogoff(pro as LogoffRequest)
            ImProtocol.Type.LOGON -> this.handleLogonResult(pro as LogonResult)
            ImProtocol.Type.REG -> this.handleRegResult(pro as RegResult)
        }
    }

    /**
     * 注册。
     * @param username  用户名
     * @param password  密码
     * @param iconIndex 头像索引
     */
    fun reg(username: String, password: String, iconIndex: Int) {
        // 发送注册请求
        val request = RegRequest(iconIndex, password, username)
        this.sendData(request)

        // 保存用户名和头像索引
        Companion.iconIndex = iconIndex
        Companion.username = username
    }

    /**
     * 接收数据的线程方法。
     */
    override fun run() {
        val receiver: ObjectInputStream // 数据接收者

        try {
            // 连接服务器
            val serverAddress = InetAddress.getByName(ImProtocol.SERVER_IP)
            super.connect(InetSocketAddress(serverAddress, ImProtocol.SERVER_PORT), Const.SERVER_CONNECT_TIME_OUT)
        } catch (_: Exception) {
            mCallback.sendEmptyMessage(Const.UM_NETWORK_ERROR)
            return
        }

        try {
            // 创建数据发送者
            mSender = ObjectOutputStream(super.getOutputStream())

            // 创建数据接收者
            receiver = ObjectInputStream(super.getInputStream())

            // 循环接收数据，直到自己被关闭
            while (!super.isClosed()) {
                // 接收数据并转换成协议
                val pro: ImProtocol = receiver.readObject() as ImProtocol

                // 处理协议
                this.onGetMessageFromServer(pro)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 发送数据。
     * @param pro   协议
     * @return 发送数据的线程
     */
    private fun sendData(pro: ImProtocol): Thread {
        val thread: Thread = SendThread(pro)
        thread.start()

        // 防止发送过快导致服务器无法响应
        try {
            Thread.sleep(100)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return thread
    }
}
